using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace Accountability4net
{
    /// <summary>
    /// Base class for all accounts
    /// </summary>
    public class BaseAccount: IAccount
    {
        /// <summary>
        /// Notify listeners that a property has changed
        /// </summary>
        /// <param name="propertyName">The name of the property that has changed</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
                PropertyChanged(this, e);
            }
        }
        
        #region IAccount Members

        /// <summary>
        /// Notification for property changes
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        private AccountType type_;
        /// <summary>
        /// The account type
        /// </summary>
        /// <seealso cref="Type"/>
        public AccountType Type
        {
            get
            {
                return type_;
            }
            set
            {
                type_ = value;
                OnPropertyChanged("Type");
            }
        }

        private int number_;
        /// <summary>
        /// The account number. Unique identifier for the account in the system.
        /// </summary>
        public int Number
        {
            get
            {
                return number_;
            }
            set
            {
                number_ = value;
                OnPropertyChanged("Number");
            }
        }

        private decimal Modify(decimal amount)
        {
            Decimal value = balance_;
            value += amount;
            balance_ = value;
            OnPropertyChanged("Balance");
            return balance_;
        }

        private void VerifyIsPositive(decimal amount)
        {
            if (amount < 0)
            {
                throw new ArgumentOutOfRangeException("You cannot modify an account with a negative value");
            }
        }

        /// <summary>
        /// Debit the account
        /// </summary>
        /// <param name="amount">The amount</param>
        /// <param name="transaction">The transaction that contained the action</param>
        /// <returns></returns>
        public decimal Debit(decimal amount, ITransaction transaction)
        {
            VerifyIsPositive(amount);
            if (Type == AccountType.LIABILITY
                || Type == AccountType.INCOME)
            {
                return Modify(-amount);
            }
            else // ASSET or EXPENSE
            {
                return Modify(amount);
            }
        }

        /// <summary>
        /// Credit the account
        /// </summary>
        /// <param name="amount">The amount</param>
        /// <param name="transaction">The transaction that contained the action</param>
        /// <returns></returns>
        public decimal Credit(decimal amount, ITransaction transaction)
        {
            VerifyIsPositive(amount);
            if (Type == AccountType.ASSET
                || Type == AccountType.EXPENSE)
            {
                return Modify(-amount);
            }
            else // LIABILITY or INCOME
            {
                return Modify(amount);
            }
        }

        Decimal balance_;
        /// <summary>
        /// Balance of the account
        /// </summary>
        public decimal Balance
        {
            get
            {
                return balance_;
            }
            set
            {
                balance_ = value;
            }
        }

        private string name_;
        /// <summary>
        /// Name for the account
        /// </summary>
        public virtual string Name
        {
            get
            {
                return name_;
            }
            set
            {
                name_ = value;
                OnPropertyChanged("Name");
            }
        }

        private string description_;
        /// <summary>
        /// Description of the account
        /// </summary>
        public virtual string Description
        {
            get
            {
                return description_;
            }
            set
            {
                description_ = value;
                OnPropertyChanged("Description");
            }
        }

        #endregion

        public override int GetHashCode()
        {
            return Number.GetHashCode()
                + Type.GetHashCode()
                + Balance.GetHashCode()
                + Name.GetHashCode()
                + Description.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            bool result = false;
            if (obj is IAccount)
            {
                IAccount account = obj as IAccount;
                result = account.Number == this.Number
                    && account.Name == this.Name
                    && account.Type == this.Type
                    && account.Description == this.Description
                    && account.Balance == this.Balance;
            }
            else
            {
                result = false;
            }
            return result;
        }

    }
}
